#include<iostream>
#include<memory>
#include <utility>
using namespace std;

/*
    这里主要练习两大部分：
        1、引用折叠
           - 引用折叠通常是出现在模板、auto自动推导或者using/typedef别名中。总结来说是两个大场景：通用引用（T&&）和多层引用组合。
           - 具体的规则是：只要有一个引用是左值引用，推到结果就是左值引用，否则推导结果就是右值引用。
        2、完美转发
           - std::forward<T>(args)：仅当T是左值引用类型时，args将会被转发为T类型的左值，否则，args将会被转发为T类型的右值。  
           - forward不会改变传入参数的类型，仍然是返回一个转发后的类型
*/

/*
    案例设计思路：
        1、定义一个函数模板，函数模板参数是一个通用引用类型（T&&）
        2、函数模板中调用一个普通函数，给这个普通函数传参时用std::forward<T>(args)
        3、这个普通函数重载多个版本，一个版本的参数是左值引用，一个版本的参数是右值引用
        =======
        这样，当调用函数模板的时候，会使用引用折叠去推导参数类型，函数模板内部则会使用std::forward<T>(args)完美转发，将参数传递给普通函数。
        普通函数会根据参数的类型，调用对应的版本。
*/

void func(int& x)
{
    cout<<"左值引用"<<endl;
}

void func(int&& x)
{
    cout<<"右值引用"<<endl;
}

template<typename T>
void testForward(T&& args)
{
    func(std::forward<T>(args));
    func(args);
    func(std::move(args));
}

int main()
{
    int x = 10;
    cout<<"============testForward(x)"<<endl;
    testForward(x);  // 输出：左值引用 左值引用 右值引用
    cout<<"============testForward(10)"<<endl;
    testForward(10);  // 输出：右值引用 左值引用 右值引用
    cout<<"============testForward(std::move(x))"<<endl;
    testForward(std::move(x));  // 输出：右值引用 左值引用 右值引用
    cout<<"============testForward(std::forward<int>(10))"<<endl;
    testForward(std::forward<int>(10));  // 输出：右值引用 左值引用 右值引用
    cout<<"============testForward(std::forward<int>(x))"<<endl;
    testForward(std::forward<int>(x)); // 输出：右值引用 左值引用 右值引用
    cout<<"============std::forward<int&>(x)"<<endl;
    // testForward(std::forward<int&>(10)); //编译报错，因为10是右值，不能绑定到左值引用上
    testForward(std::forward<int&>(x)); // 输出：左值引用 左值引用 右值引用
    cout<<"=============testForward(std::forward<int&&>(x))"<<endl;
    testForward(std::forward<int&&>(x)); // 输出：右值引用 左值引用 右值引用
    cout<<"=============testForward(std::forward<int&&>(10))"<<endl;
    testForward(std::forward<int&&>(10)); // 输出：右值引用 左值引用 右值引用
    cout<<"============="<<endl;

    return 0;
}